package com.xc.disk.web.rest;

import com.xc.disk.bean.DevFileBean;
import com.xc.disk.bean.FileBean;
import com.xc.disk.bean.FolderFileBean;
import com.xc.disk.config.Constants;
import com.xc.disk.dto.FileDto;
import com.xc.disk.dto.UploadDto;
import com.xc.disk.dto.UrlDto;
import com.xc.disk.dto.UserDto;
import com.xc.disk.entity.FileEntity;
import com.xc.disk.entity.UserEntity;
import com.xc.disk.enums.RedisLock;
import com.xc.disk.exception.OperateException;
import com.xc.disk.info.SignInfo;
import com.xc.disk.info.SourceInfo;
import com.xc.disk.info.TokenInfo;
import com.xc.disk.service.FileService;
import com.xc.disk.service.FolderService;
import com.xc.disk.service.UserService;
import com.xc.disk.utils.RedisUtils;
import com.xc.tool.utils.JSONUtils;
import com.xc.tool.utils.Md5Utils;
import com.xc.tool.utils.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;
import java.util.UUID;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: 文件管理接口</p>
 * <P>创建人:小程 </p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/9/27</p>
 *
 * @version v1.0
 */
@RestController
public class FileRest {

    @Autowired
    private FileService fileService;
    @Autowired
    private Constants constants;
    @Autowired
    private UserService userService;
    @Autowired
    private FolderService folderService;


    /**
     * 创建文件
     *
     * @param userId   用户id
     * @param fileBean 文件参数
     * @return 文件信息
     */
    private UploadDto createFile(String userId, FileBean fileBean) {
        UploadDto uploadDto = new UploadDto();
        //保存文件信息到redis
        FileDto fileDto = new FileDto();
        fileDto.setName(fileBean.getName());
        fileDto.setHashCode(fileBean.getHashCode());
        fileDto.setUserId(userId);
        fileDto.setSize(fileBean.getSize());
        fileDto.setFolderId(fileBean.getFolderId());
        fileDto.setFolders(fileBean.getFolders());
        //检测文件源
        SourceInfo sourceInfo = fileService.getSource(fileBean.getHashCode());
        if (sourceInfo == null) {
            sourceInfo = fileService.getSourceInfo(fileBean.getSize());
            if (sourceInfo == null) {
                throw new OperateException("f009", "没有可存放文件的服务！");
            }
            //生成fid
            String uploadId = Md5Utils.getSaltMd5(UUID.randomUUID().toString());
            RedisUtils.set(uploadId, fileDto, 43200000l);
            //返回参数
            uploadDto.setFinish(false);
            uploadDto.setUploadUrl(sourceInfo.getLocalUrl() + constants.getUploadUrl());
            uploadDto.setHashCode(fileBean.getHashCode());
            uploadDto.setUploadId(uploadId);
            return uploadDto;
        } else {
            FileEntity fileEntity;
            try {
                if (!RedisUtils.lock(RedisLock.FILE_LOCK.getKey(), System.currentTimeMillis())) {
                    throw new OperateException("f054", "更新用户及文件信息超时！");
                }
                fileDto.setSize(fileBean.getSize());
                fileEntity = fileService.createFileInfo(fileDto);
            } finally {
                RedisUtils.unlock(RedisLock.FILE_LOCK.getKey());
            }
            uploadDto.setFid(fileEntity.getId());
            uploadDto.setFinish(true);
            uploadDto.setHashCode(fileBean.getHashCode());
            return uploadDto;
        }
    }

    /**
     * 创建文件
     *
     * @param token    用户token信息
     * @param fileBean 文件参数
     * @return 文件信息
     */
    @PostMapping("/file")
    public UploadDto createFile(TokenInfo<UserDto> token, @RequestBody FileBean fileBean) {
        return createFile(token.getUserId(), fileBean);
    }

    /**
     * 移动文件
     *
     * @param token       用户token信息
     * @param folderFiles 文件目录id集合
     */
    @PutMapping(value = "/file/folder/move")
    public void moveFileFolder(TokenInfo token, @RequestBody List<FolderFileBean> folderFiles) {
        fileService.moveFileFolder(token, folderFiles);
    }

    /**
     * 复制文件
     *
     * @param token       用户token信息
     * @param folderFiles 文件目录id集合
     */
    @PutMapping(value = "/file/folder/copy")
    public void copyFileFolder(TokenInfo token, @RequestBody List<FolderFileBean> folderFiles) {
        fileService.copyFileFolder(token, folderFiles);
    }

    /**
     * 登录后获取下载文件地址
     *
     * @param token 用户token
     * @param fid   文件id
     */
    @GetMapping("/download/url/{fid}")
    public UrlDto downloadFile(TokenInfo<UserDto> token, @PathVariable("fid") String fid, HttpServletResponse response) {
        FileEntity fileEntity = fileService.getFileById(fid);
        if (!token.getUserId().equals(fileEntity.getUserId())) {
            throw new OperateException("f027", "你的账号没有操作该数据的权限！");
        }
        String url = fileService.getDownloadUrl(fileEntity);
        UrlDto urlDto = new UrlDto();
        urlDto.setUrl(url);
        return urlDto;
    }

    /**
     * 修改文件
     *
     * @param token 用户token
     * @param id    文件id
     * @param name  文件名称
     * @return 修改后的文件信息
     */
    @PutMapping(value = "/file/{id}")
    public FileEntity updateFile(TokenInfo<UserDto> token, @PathVariable("id") String id, @RequestParam String name) {
        return fileService.updateFile(token, id, name);
    }


    /**
     * 开发者创建文件
     *
     * @param devFileBean 文件参数
     * @return 文件信息
     */
    @PostMapping("/file/dev")
    public UploadDto createDevFile(@RequestBody DevFileBean devFileBean) {
        //验证签名是否有效
        if ((new Date().getTime() - Long.parseLong(devFileBean.getTimeStamp())) > constants.getSignExpiryTime()) {
            throw new OperateException("f045", "签名验证失败，你的签名超时！");
        }
        UserEntity userEntity = userService.getUserByAppId(devFileBean.getAppId());
        if (userEntity == null) {
            throw new OperateException("f044", "appId不正确！");
        }
        TreeMap<String, Object> treeMap = new TreeMap<>();
        treeMap.put("appId", devFileBean.getAppId());
        treeMap.put("folders", devFileBean.getFolders());
        treeMap.put("hashCode", devFileBean.getHashCode());
        treeMap.put("name", devFileBean.getName());
        treeMap.put("randomStr", devFileBean.getTimeStamp());
        treeMap.put("size", devFileBean.getSize());
        treeMap.put("timeStamp", devFileBean.getTimeStamp());
        String json = JSONUtils.getStringByObject(treeMap);
        if (!Md5Utils.verifySaltMd5(json, devFileBean.getSign(), userEntity.getAppSecret())) {
            throw new OperateException("f008", "签名验证失败，你的签名不正确！");
        }
        FileBean fileBean = ObjectUtils.convert(new FileBean(), devFileBean);
        fileBean.getFolders().add(0, constants.getDevFolder());
        return createFile(userEntity.getId(), fileBean);
    }

    /**
     * 开发文件下载
     *
     * @param fid  文件id
     * @param open 浏览器是否可以打开
     */
    @GetMapping("/download/dev/{fid}")
    public void downloadDevFile(@PathVariable("fid") String fid,
                                @RequestParam(value = "open", required = false) Integer open,
                                HttpServletRequest request, HttpServletResponse response) {
        try {
            if (fid.contains(".")) {
                fid = fid.substring(0, fid.lastIndexOf("."));
            }
            FileEntity fileEntity = fileService.getDevFile(request, fid);
            String url = fileService.getDownloadUrl(fileEntity);
            response.setHeader("Access-Control-Allow-Origin", "*");
            if (open == null) {
                response.sendRedirect(url + "&open=0");
            } else {
                response.sendRedirect(url + "&open=" + open);
            }
        } catch (IOException e) {
            throw new OperateException("f015", "重定向下载失败！");
        }
    }

    /**
     * 删除开发文件
     *
     * @param ids
     */
    @DeleteMapping("/file/dev/{ids}")
    public void deleteDevFile(@PathVariable List<String> ids, @ModelAttribute SignInfo signInfo) {
        //验证签名是否有效
        if ((new Date().getTime() - Long.parseLong(signInfo.getTimeStamp())) > constants.getSignExpiryTime()) {
            throw new OperateException("f045", "签名验证失败，你的签名超时！");
        }
        UserEntity userEntity = userService.getUserByAppId(signInfo.getAppId());
        if (userEntity == null) {
            throw new OperateException("f044", "appId不正确！");
        }
        TreeMap<String, Object> treeMap = new TreeMap<>();
        treeMap.put("appId", signInfo.getAppId());
        treeMap.put("ids", ids);
        treeMap.put("randomStr", signInfo.getTimeStamp());
        treeMap.put("timeStamp", signInfo.getTimeStamp());
        String json = JSONUtils.getStringByObject(treeMap);
        if (!Md5Utils.verifySaltMd5(json, signInfo.getSign(), userEntity.getAppSecret())) {
            throw new OperateException("f008", "签名验证失败，你的签名不正确！");
        }
        fileService.deleteDevFile(ids, userEntity.getId());
    }


}
